﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using EAS_Protocol.Helpers;
using EAS_Protocol.Models;
using EAS_Protocol;
using System.IO;
using System.Xml;
using System.Text;
using System.Xml.XPath;

namespace EAS_Web_API.Controllers
{
    /// <summary>
    /// API for manipulating the Contacts folder
    /// </summary>
    public class ContactsController : ApiController
    {
        /// <summary>
        /// Syncs all contacts in the "Default Contacts" folder.
        /// </summary>
        /// <param name="serverAddress">The address of the Exchange ActiveSync Server. Should be in the form "http(s)://FQDN"</param>
        /// <param name="credentials">Credentials for an EAS user. Format => base64(username:password).</param>
        /// <param name="DeviceID">The unique id of the "device".</param>
        /// <param name="DeviceType">The type of the "device".</param>
        /// <returns>List&lt;Contact&gt;.</returns>
        [HttpGet]
        [AllowAnonymous]
        public IHttpActionResult syncContacts(string serverAddress, string credentials, string DeviceID, string DeviceType)
        {       
            //The algorithm for syncing all contacts is a three-step process:
            // - Do a FolderSync to get the Ids for all folders, and identify the folder for Default contacts
            // - Do a Sync with a SyncKey == 0 to get a new SyncKey
            // - Do a Sync with the new SyncKey

            List<Contact> contacts = new List<Contact>();

            string strUsername = HelperMethods.getUsername(credentials);
            string strDeviceID = DeviceID;
            string strDeviceType = DeviceType;
            string creds = credentials;

            string easUrl = serverAddress;

            string syncKey = "0";
            string collectionId = "";

            string ASVersion;
            string UserAgent;

            //Filter out necessary values from headers
            //Fall back to defaults if not provided
            //To do : Should defaults be provided or should the API throw an error?
            var headerValues = new List<string>().AsEnumerable();
            if (Request.Headers.TryGetValues("MS-ASProtocolVersion", out headerValues))
            {
                ASVersion = headerValues.First().ToString();
            }
            else
            {
                ASVersion = "14.1";
            }
            if (Request.Headers.TryGetValues("DeviceUserAgent", out headerValues))
            {
                UserAgent = headerValues.First().ToString();
            }
            else
            {
                UserAgent = "EASWeb";
            }


            Device dev = new Device();
            dev.ASVersion = ASVersion;
            dev.DeviceID = DeviceID;
            dev.DeviceType = DeviceType;
            dev.UserAgent = UserAgent;
            dev.Username = strUsername;
            dev.ServerAddress = easUrl;
            dev.Credentials = credentials;

            string fsResponse = AS_CMD.FolderSync(dev);
            collectionId = AS_CMD.GetCollectionID(fsResponse, FolderSyncTypes.Default_Contacts);
            syncKey = AS_CMD.Sync(dev, collectionId);
            string syncResponse = AS_CMD.Sync(dev, syncKey, collectionId);            

            try
            {
                //We parse out the contact elements
                var xmlSync = new XmlDocument();
                xmlSync.LoadXml(syncResponse);

                //Check the status of the sync request
                var status = xmlSync.GetElementsByTagName("Status");

                //Status of "1" == Success
                if (status[0].InnerXml == "1")
                {
                    var contactResults = xmlSync.GetElementsByTagName("ApplicationData");
                    int contactItems = contactResults.Count;

                    #region                    
                    for (int i = 0; i < contactItems; i++)
                    {                        
                        contacts.Add(new Contact());
                        //A given contact may have empty/null properties 
                        //These tags will not be included in the XML
                        //so we need to check the count for each xmlItem before adding the value.
                        var appDataNodes = contactResults.Item(i).ChildNodes;

                        for (int j=0; j<appDataNodes.Count;j++)
                        {
                            var itemName = appDataNodes.Item(j).Name;
                            var itemValue = appDataNodes.Item(j).InnerText;

                            if (itemName == "AssistantName")                            
                                contacts[i].AssistantName = itemValue;                            
                            if (itemName == "AssistantPhoneNumber")
                                contacts[i].AssistantPhoneNumber = itemValue;
                            if (itemName == "Birthday")
                                contacts[i].Birthday = itemValue;
                            if (itemName == "Business2PhoneNumber")
                                contacts[i].Business2PhoneNumber = itemValue;
                            if (itemName == "BusinessAddressCity")
                                contacts[i].BusinessAddressCity = itemValue;
                            if (itemName == "BusinessAddressCountry")
                                contacts[i].BusinessAddressCountry = itemValue;
                            if (itemName == "BusinessAddressPostalCode")
                                contacts[i].BusinessAddressPostalCode = itemValue;
                            if (itemName == "BusinessAddressState")
                                contacts[i].BusinessAddressState = itemValue;
                            if (itemName == "BusinessAddressStreet")
                                contacts[i].BusinessAddressStreet = itemValue;
                            if (itemName == "BusinessFaxNumber")
                                contacts[i].BusinessFaxNumber = itemValue;
                            if (itemName == "BusinessPhoneNumber")
                                contacts[i].BusinessPhoneNumber = itemValue;
                            if (itemName == "CarPhoneNumber")
                                contacts[i].CarPhoneNumber = itemValue;
                            if (itemName == "Categories")
                                contacts[i].Categories = itemValue;
                            if (itemName == "Category")
                                contacts[i].Category = itemValue;
                            if (itemName == "Children")
                                contacts[i].Children = itemValue;
                            if (itemName == "Child")
                                contacts[i].Child = itemValue;
                            if (itemName == "CompanyName")
                                contacts[i].CompanyName = itemValue;
                            if (itemName == "Department")
                                contacts[i].Department = itemValue;
                            if (itemName == "Email1Address")
                                contacts[i].Email1Address = itemValue;
                            if (itemName == "Email2Address")
                                contacts[i].Email2Address = itemValue;
                            if (itemName == "Email3Address")
                                contacts[i].Email3Address = itemValue;
                            if (itemName == "FileAs")
                                contacts[i].FileAs = itemValue;
                            if (itemName == "FirstName")
                                contacts[i].FirstName = itemValue;
                            if (itemName == "Home2PhoneNumber")
                                contacts[i].Home2PhoneNumber = itemValue;
                            if (itemName == "HomeAddressCity")
                                contacts[i].HomeAddressCity = itemValue;
                            if (itemName == "HomeAddressCountry")
                                contacts[i].HomeAddressCountry = itemValue;
                            if (itemName == "HomeAddressPostalCode")
                                contacts[i].HomeAddressPostalCode = itemValue;
                            if (itemName == "HomeAddressState")
                                contacts[i].HomeAddressState = itemValue;
                            if (itemName == "HomeAddressStreet")
                                contacts[i].HomeAddressStreet = itemValue;
                            if (itemName == "HomeFaxNumber")
                                contacts[i].HomeFaxNumber = itemValue;
                            if (itemName == "HomePhoneNumber")
                                contacts[i].HomePhoneNumber = itemValue;
                            if (itemName == "JobTitle")
                                contacts[i].JobTitle = itemValue;
                            if (itemName == "LastName")
                                contacts[i].LastName = itemValue;
                            if (itemName == "MiddleName")
                                contacts[i].MiddleName = itemValue;
                            if (itemName == "MobilePhoneNumber")
                                contacts[i].MobilePhoneNumber = itemValue;
                            if (itemName == "OfficeLocation")
                                contacts[i].OfficeLocation = itemValue;
                            if (itemName == "OtherAddressCity")
                                contacts[i].OtherAddressCity = itemValue;
                            if (itemName == "OtherAddressCountry")
                                contacts[i].OtherAddressCountry = itemValue;
                            if (itemName == "OtherAddressPostalCode")
                                contacts[i].OtherAddressPostalCode = itemValue;
                            if (itemName == "OtherAddressState")
                                contacts[i].OtherAddressState = itemValue;
                            if (itemName == "OtherAddressStreet")
                                contacts[i].OtherAddressStreet = itemValue;
                            if (itemName == "PagerNumber")
                                contacts[i].PagerNumber = itemValue;
                            if (itemName == "RadioPhoneNumber")
                                contacts[i].RadioPhoneNumber = itemValue;
                            if (itemName == "Spouse")
                                contacts[i].Spouse = itemValue;
                            if (itemName == "Suffix")
                                contacts[i].Suffix = itemValue;
                            if (itemName == "Title")
                                contacts[i].Title = itemValue;
                            if (itemName == "WebPage")
                                contacts[i].WebPage = itemValue;
                            if (itemName == "WeightedRank")
                                contacts[i].WeightedRank = itemValue;
                            if (itemName == "YomiCompanyName")
                                contacts[i].YomiCompanyName = itemValue;
                            if (itemName == "YomiFirstName")
                                contacts[i].YomiFirstName = itemValue;
                            if (itemName == "YomiLastName")
                                contacts[i].YomiLastName = itemValue;
                            if (itemName == "Picture")
                                contacts[i].Picture = itemValue;
                            if (itemName == "Alias")
                                contacts[i].Alias = itemValue;                            
                        }                        
                    }
                    //#endregion

                }
                //Status !1 == !OK
                else
                {
                    //ToDo: work the switch case into a lookup to some common status code handling.

                    //http://msdn.microsoft.com/en-us/library/gg675457(v=exchg.80).aspx
                    switch (status[0].InnerXml)
                    {
                        //Invalid synchronization key.
                        case "3":
                            break;

                        //Protocol error
                        case "4":
                            break;

                        //Server error
                        case "5":
                            break;

                        //Error in client/server conversion
                        case "6":
                            break;

                        //Conflict matching the client and server object
                        case "7":
                            break;

                        //Object not found
                        case "8":
                            break;

                        //The Sync command cannot be completed
                        case "9":
                            break;

                        //The folder hierarchy has changed
                        case "12":
                            break;

                        //The Sync command request is not complete
                        case "13":
                            break;

                        //Invalid Wait or HeartbeatInterval value
                        case "14":
                            break;

                        //Invalid Sync command request
                        case "15":
                            break;

                        //Retry
                        case "16":
                            break;

                        default:
                            break;
                    }
                }
            }
            catch (Exception)
            {
            }            
            #endregion

            if (contacts.Count == 0)
                return StatusCode(HttpStatusCode.NoContent);
            else
                return Ok(contacts);
        }
        
    }
}
